#include "fifo.h"
#include "common.h"
#include "init.h"
#include <linux/slab.h>

struct kfifo_cfg {
	// init block
	struct init_block init;

	// kfifo struct
	struct kfifo fifo;

	// fifo size
	size_t size;
};

static int init_kfifo(struct init_block *init) 
{
	struct kfifo_cfg *cfg = container_of(init, struct kfifo_cfg, init);
	int ret = kfifo_alloc(&cfg->fifo, cfg->size, GFP_KERNEL);
	if (ret) {
		PR_ERR("alloc kfifo failed, errno %d", ret);
	}
	return ret;
}

static void cleanup_kfifo(struct init_block *init) 
{
	struct kfifo_cfg *cfg = container_of(init, struct kfifo_cfg, init);
	kfifo_free(&cfg->fifo);
}

static void drop_kfifo(struct init_block *init)
{
	struct kfifo_cfg *cfg = container_of(init, struct kfifo_cfg, init);
	kfree(cfg);
}

int new_kfifo(size_t size, const char *name, struct kfifo **fifo)
{
	int ret = 0;
	struct init_block *init = NULL;
	struct kfifo_cfg *cfg = kmalloc(sizeof(struct kfifo_cfg), GFP_KERNEL);
	if (cfg == NULL) {
		PR_ERR("can't alloc kfifo_cfg");
		cleanup();
		return -1;
	}

	cfg->size = size;

	init = &cfg->init;
	init->init_fn = init_kfifo;
	init->drop_fn = drop_kfifo;
	init->cleanup_fn = cleanup_kfifo;
	strncpy(init->name, name, BLOCK_NAME_LEN);

	ret = try_init(init);
	if (ret == 0) {
		*fifo = &cfg->fifo;
	} else {
		*fifo = NULL;
	}
	return ret;
}

